﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;

namespace _3D_FileExplorer
{
    public  abstract class Primitives
    {
        #region GLOBAL_VARIABLES
        public List<VertexPositionColorTexture> vertices = new List<VertexPositionColorTexture>();
        public List<ushort> indices = new List<ushort>();
        public VertexBuffer vertexBuffer;
        public IndexBuffer indexBuffer;
        public Matrix rotation = Matrix.Identity;
        public Matrix translation = Matrix.Identity;
        public Matrix fulltransform = Matrix.Identity;
        public string name;
        public GraphicsDevice device;
        public bool picked;
        public BoundingSphere boudingSphere;
        public string shortName;
        public string type;
        public Vector3 center;
        Color color;
        public long id;
        public float weight;
       
        //Variables for picking
        public List<Vector3> pvertices = new List<Vector3>();

        //BigBufferVariables
        public static List<VertexPositionColorTexture> bigCylinderVertices = new List<VertexPositionColorTexture>();
        public static List<int> bigCylinderIndices = new List<int>();
        public static int cylinderPrimitivesCount = -1;
        public static List<VertexPositionColorTexture> bigCubeVertices = new List<VertexPositionColorTexture>();
        public static List<int> bigCubeIndices = new List<int>();
        public static int cubePrimitivesCount = -1;
        #endregion


        public Primitives(Matrix translation, Matrix rotation, Color color, string name,string shortName,string type,long id,float weight)
        {
            this.id = id;
            this.translation = translation;
            this.rotation = rotation;
            this.color = color;
            this.name = name;     
            this.type = type;
            this.shortName = shortName;
            this.weight = weight;
        }


        public void ChangeColor(Color color)
        {
            int i;
            for (i = 0; i < vertices.Count; i++)
            {
                vertices[i] = new VertexPositionColorTexture(vertices[i].Position, color, new Vector2(0, 0));
            }
            vertexBuffer.SetData(vertices.ToArray(), 0, vertices.ToArray().Length);
        }

        public void CreateBoudingSphere()
        {
            boudingSphere = BoundingSphere.CreateFromPoints(pvertices);
            center = Vector3.Transform(boudingSphere.Center, fulltransform);
        }


        public virtual void LoadBuffer()
        {
            //allocate memory in vertex buffers
            vertexBuffer = new VertexBuffer(Fe.graphics.GraphicsDevice,typeof(VertexPositionColorTexture),vertices.Count, BufferUsage.None);
            indexBuffer = new IndexBuffer(Fe.graphics.GraphicsDevice, typeof(ushort), indices.Count, BufferUsage.None);
            //set data to vertex buffers
            vertexBuffer.SetData(vertices.ToArray(),0,vertices.ToArray().Length);
            indexBuffer.SetData(indices.ToArray());

        }


        public virtual void Draw(BasicEffect effect, Camera camera, GraphicsDevice Device)
        {

            //Tell the GPU what vertex array to pull vertex data from.
            Device.Indices = indexBuffer;
            Device.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionColorTexture.SizeInBytes);

            int primitiveCount = indices.Count / 3;

            Device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Count, 0, primitiveCount);

        }


    }

    public class Cylinder : Primitives
    {

        public float height;
        public float radius;
        public int faces;
        Vector3 origin;
        public bool isLast = false;
        public int branchLevel;
        public List<VertexPositionColorTexture> topBaseLines = new List<VertexPositionColorTexture>();
        public List<VertexPositionColorTexture> bottomBaseLines = new List<VertexPositionColorTexture>();

        public Cylinder(Matrix translation, Matrix rotation, Color color, string name, string shortName, string type,long id,float weight, int isLast, float height, float radius, int faces, Vector3 origin,int branchLevel)
            : base(translation, rotation, color, name, shortName,type,id,weight)
        {
            this.height = height;
            this.radius = radius;
            this.faces = faces;
            this.origin = origin;
            this.branchLevel = branchLevel;
            if (isLast == 0)
            {
                this.isLast = true;
            }
            cylinderPrimitivesCount++;
            CreateVertices(height, radius, faces, origin, color);
            CreateIndices();
            LoadBuffer();
            CreateBoudingSphere();
        }
        private void CreateVertices(float height, float radius, int faces, Vector3 origin, Color color)
        {
            int i;
            int j = 0;
            float fullangle = 0;
            float degree;
            float angle = MathHelper.TwoPi / (float)faces;
            //Cilinder origin 0,0,0
            fulltransform = rotation * translation;
            vertices.Add(new VertexPositionColorTexture(Vector3.Transform(new Vector3(0, 0, 0), fulltransform), Color.White, new Vector2(0, 0)));
            bigCylinderVertices.Add(new VertexPositionColorTexture(Vector3.Transform(new Vector3(0, 0, 0), fulltransform), Color.White, new Vector2(0, 0)));
            pvertices.Add(new Vector3(0, 0, 0));
            this.origin = Vector3.Transform(new Vector3(0, 0, 0), fulltransform);

            vertices.Add(new VertexPositionColorTexture(Vector3.Transform(new Vector3(0, 0, 0) + (Vector3.Up * new Vector3(0, height, 0)), fulltransform), Color.White, new Vector2(0, 0)));
            bigCylinderVertices.Add(new VertexPositionColorTexture(Vector3.Transform(new Vector3(0, 0, 0) + (Vector3.Up * new Vector3(0, 0.9999f * height, 0)), fulltransform), Color.White, new Vector2(0, 0)));
            pvertices.Add(new Vector3(0, 0, 0) + (Vector3.Up * new Vector3(0, height, 0)));
            //Create vertexs on circle
            for (i = 0; i < faces; i++)
            {
                fullangle += angle;
                degree = MathHelper.ToDegrees(fullangle);
                float dx = (float)Math.Cos(fullangle);
                float dz = (float)Math.Sin(fullangle);

                //Bottom vertex
                vertices.Add(new VertexPositionColorTexture(Vector3.Transform(new Vector3(dx, 0, dz) * radius, fulltransform), color, new Vector2(0, 0)));
                bigCylinderVertices.Add(new VertexPositionColorTexture(Vector3.Transform(new Vector3(dx, 0, dz) * radius, fulltransform), color, new Vector2(0, 0)));
                pvertices.Add(new Vector3(dx, 0, dz) * radius);
                bottomBaseLines.Add(new VertexPositionColorTexture(Vector3.Transform(new Vector3(dx, 0, dz) * 0.99f * radius, fulltransform), color, new Vector2(0, 0)));
                //Top vertex
                vertices.Add(new VertexPositionColorTexture(Vector3.Transform(new Vector3(dx, 0, dz) * radius + (Vector3.Up * new Vector3(0, height, 0)), fulltransform), color, new Vector2(0, 0)));
                bigCylinderVertices.Add(new VertexPositionColorTexture(Vector3.Transform(new Vector3(dx, 0, dz) * 0.99f * radius + (Vector3.Up * new Vector3(0, 0.9999f * height, 0)), fulltransform), color, new Vector2(0, 0)));
                pvertices.Add(new Vector3(dx, 0, dz) * radius + (Vector3.Up * new Vector3(0, height, 0)));
                topBaseLines.Add(new VertexPositionColorTexture(Vector3.Transform(new Vector3(dx, 0, dz) * radius + (Vector3.Up * new Vector3(0, height, 0)), fulltransform), color, new Vector2(0, 0)));
            }

            topBaseLines.Add(topBaseLines[0]);
            bottomBaseLines.Add(bottomBaseLines[0]);
        }
        private void CreateIndices()
        {
            int i = 0;
            for (i = 0; i < faces; i++)
            {
                //bottom indices
                AddIndex(0);
                AddIndex(i * 2 + 2);
                if (i != (faces - 1))
                {
                    AddIndex(i * 2 + 4);
                }
                else
                {
                    AddIndex(2);
                }
                //bottom indices
                AddIndex(1);
                if (i != (faces - 1))
                {
                    AddIndex(i * 2 + 5);
                }
                else
                {
                    AddIndex(3);
                }
                AddIndex(i * 2 + 3);
                //one half of coat
                AddIndex(i * 2 + 2);
                if (i != (faces - 1))
                {
                    AddIndex(i * 2 + 5);
                    AddIndex(i * 2 + 4);
                }
                else
                {
                    AddIndex(3);
                    AddIndex(2);
                }

                //second half of coat
                AddIndex(i * 2 + 2);
                AddIndex(i * 2 + 3);
                if (i != (faces - 1))
                {
                    AddIndex(i * 2 + 5);
                }
                else
                {
                    AddIndex(3);
                }
            }
            indices.Reverse();
        }

        public void AddIndex(int index)
        {
            indices.Add((ushort)index);
            bigCylinderIndices.Add(index + bigCylinderVertices.Count - vertices.Count);
        }


    }

    public class Cube : Primitives
    {

        public Vector3 Center;
        public string ancestorsName;
        public Matrix ancestorsTranslation;
        public Matrix ancestorsRotation;

        public Cube(Matrix translation, Matrix rotation, Color color, string name,string shortName,string type,long id,float weight,float cubeSide,string ancestorsName,Matrix ancestorsTranslation)
        : base(translation, rotation, color, name, shortName,type,id,weight)
        {
            this.ancestorsTranslation = ancestorsTranslation;
            this.ancestorsRotation = rotation;
            this.ancestorsName = ancestorsName;
            cubePrimitivesCount++;
            CreateVertices(cubeSide);
            LoadBuffer();
            CreateBoudingSphere();
        }

        private void CreateVertices(float cubeSide)
        {

            float textureY = 1f / 4f;
            float textureX = 1f / 2f;

            float indexX, indexY;
            RotationOptimizing();

             //indexX = TexturesDefinition.GetCoordinates(type).X;
             //indexY = TexturesDefinition.GetCoordinates(type).Y;
            indexX = Fe.td.GetX(type);
            indexY = Fe.td.GetY(type);

            fulltransform = Matrix.CreateScale(cubeSide) * rotation * translation;
            //side1
            AddVertex(new Vector3(1.3f, -1, 1.3f), new Vector2(textureX * indexX, textureY * (indexY - 1)));
            AddVertex(new Vector3(-1.3f, -1, 1.3f), new Vector2(textureX * indexX, textureY * indexY));
            AddVertex(new Vector3(-1.3f, 1, 1.3f), new Vector2(textureX * (indexX - 1), textureY * indexY));
            AddVertex(new Vector3(1.3f, 1, 1.3f), new Vector2(textureX * (indexX - 1), textureY * (indexY - 1)));

            AddIndex(0); AddIndex(1); AddIndex(3); AddIndex(3); AddIndex(1); AddIndex(2);

            //side2
            AddVertex(new Vector3(1.3f, -1, -1.3f), new Vector2(textureX * (indexX - 1), textureY * (indexY - 1)));
            AddVertex(new Vector3(-1.3f, -1, -1.3f), new Vector2(textureX * (indexX - 1), textureY * indexY));
            AddVertex(new Vector3(-1.3f, 1, -1.3f), new Vector2(textureX * indexX, textureY * indexY));
            AddVertex(new Vector3(1.3f, 1, -1.3f), new Vector2(textureX * indexX, textureY * (indexY - 1)));

            AddIndex(4); AddIndex(7); AddIndex(5); AddIndex(7); AddIndex(6); AddIndex(5);

            //side3
            AddVertex(new Vector3(1.3f, -1, -1.3f), new Vector2(textureX * (indexX - 1), textureY * indexY));
            AddVertex(new Vector3(1.3f, -1, 1.3f), new Vector2(textureX * (indexX - 1), textureY * (indexY - 1)));
            AddVertex(new Vector3(1.3f, 1, 1.3f), new Vector2(textureX * indexX, textureY * (indexY - 1)));
            AddVertex(new Vector3(1.3f, 1, -1.3f), new Vector2(textureX * indexX, textureY * indexY));

            AddIndex(8); AddIndex(9); AddIndex(10); AddIndex(10); AddIndex(11); AddIndex(8);

            //side4
            AddVertex(new Vector3(-1.3f, -1, -1.3f), new Vector2(textureX * indexX, textureY * indexY));
            AddVertex(new Vector3(-1.3f, -1, 1.3f), new Vector2(textureX * indexX, textureY * (indexY - 1)));
            AddVertex(new Vector3(-1.3f, 1, 1.3f), new Vector2(textureX * (indexX - 1), textureY * (indexY - 1)));
            AddVertex(new Vector3(-1.3f, 1, -1.3f), new Vector2(textureX * (indexX - 1), textureY * indexY));
            AddIndex(12); AddIndex(14); AddIndex(13); AddIndex(14); AddIndex(12); AddIndex(15);

            //side5 - no texture
            AddVertex(new Vector3(1.3f, -1, 1.3f), new Vector2(0, 0));
            AddVertex(new Vector3(-1.3f, -1, 1.3f), new Vector2(0, 0));
            AddVertex(new Vector3(-1.3f, -1, -1.3f), new Vector2(0, 0));
            AddVertex(new Vector3(1.3f, -1, -1.3f), new Vector2(0, 0));
            AddIndex(16); AddIndex(19); AddIndex(18); AddIndex(16); AddIndex(18); AddIndex(17);

            //side6 - no texture
            AddVertex(new Vector3(1.3f, 1, 1.3f), new Vector2(0, 0));
            AddVertex(new Vector3(-1.3f, 1, 1.3f), new Vector2(0, 0));
            AddVertex(new Vector3(-1.3f, 1, -1.3f), new Vector2(0, 0));
            AddVertex(new Vector3(1.3f, 1, -1.3f), new Vector2(0, 0));
            AddIndex(20); AddIndex(21); AddIndex(22); AddIndex(22); AddIndex(23); AddIndex(20);

        }

        private void RotationOptimizing()
        {

            if (Math.Abs(rotation.Up.Z) == 1)
            {
                rotation = Matrix.CreateRotationX(-MathHelper.PiOver2);
            }

            if (Math.Abs(rotation.Up.Y) == 1)
            {
                rotation = Matrix.CreateRotationX(-MathHelper.Pi) * Matrix.CreateRotationY(MathHelper.PiOver2);
            }
            if (Math.Abs(rotation.Up.X) == 1)
            {
                rotation = Matrix.CreateRotationZ(MathHelper.PiOver2) * Matrix.CreateRotationX(-MathHelper.PiOver2);
            }

        }

        public void AddVertex(Vector3 vertice, Vector2 coord)
        {
            vertices.Add(new VertexPositionColorTexture(Vector3.Transform(vertice, fulltransform), Color.White, coord));
            bigCubeVertices.Add(new VertexPositionColorTexture(Vector3.Transform(vertice, fulltransform), Color.White, coord));
            pvertices.Add(vertice);
        }


        public void AddIndex(int index)
        {
            indices.Add((ushort)index);
            bigCubeIndices.Add(index + bigCubeVertices.Count - vertices.Count);
        }

    }
}

